package com.horizon.service.endpoint.service;

import com.horizon.common.constrants.HorizonResultCodeEnum;
import com.horizon.common.exception.HorizonException;
import com.horizon.common.util.GsonUtil;
import com.horizon.manage.endpoint.entity.Endpoint;
import com.horizon.manage.endpoint.service.IEndpointService;
import com.horizon.service.endpoint.dto.EndpointOperation;
import lombok.AllArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 终端操作服务.
 *
 * @author zhaoshb
 * @since 2023/1/12 08:12
 */
@Service
@AllArgsConstructor
public class EndpointOperationService {

    private static final String ENDPOINT_OPERATION_REDIS_KEY_FORMAT = "ENDPOINT_OPERATION_%d";

    private StringRedisTemplate stringRedisTemplate;

    private IEndpointService endpointService;

    public List<EndpointOperation> popOperation(String tenantId, Long endpointId) throws HorizonException {
        Endpoint endpoint = endpointService.queryEndpointById(endpointId);
        if (!StringUtils.equalsIgnoreCase(tenantId, endpoint.getTenantId())) {
            throw new HorizonException(HorizonResultCodeEnum.ENDPOINT_NO_PERMISSION_DO_OPERATION);
        }
        return stringRedisTemplate.execute(new PopOperationAction(endpointId));
    }

    public Boolean pushOperation(Long endpointId, List<EndpointOperation> operationList) {
        return stringRedisTemplate.execute(new PushOperationAction(endpointId, operationList));
    }

    private static class PopOperationAction implements RedisCallback<List<EndpointOperation>> {

        private final String key;

        public PopOperationAction(Long endpointId) {
            this.key = String.format(ENDPOINT_OPERATION_REDIS_KEY_FORMAT, endpointId);
        }

        @Override
        public List<EndpointOperation> doInRedis(RedisConnection connection) throws DataAccessException {
            List<byte[]> resultList = connection.lRange(key.getBytes(StandardCharsets.UTF_8), 0, -1);
            if (CollectionUtils.isEmpty(resultList)) {
                return Collections.EMPTY_LIST;
            }
            return resultList.stream().map(result -> GsonUtil.fromJson(new String(result), EndpointOperation.class)).collect(Collectors.toList());
        }
    }

    private static class PushOperationAction implements RedisCallback<Boolean> {

        private final String key;

        private final List<EndpointOperation> operationList;

        public PushOperationAction(Long endpointId, List<EndpointOperation> operationList) {
            this.key = String.format(ENDPOINT_OPERATION_REDIS_KEY_FORMAT, endpointId);
            this.operationList = operationList;
        }

        @Override
        public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
            List<byte[]> optBytesList = operationList.stream().map(GsonUtil::toJsonBytes).collect(Collectors.toList());
            Long pushNumbers = connection.rPush(key.getBytes(StandardCharsets.UTF_8), optBytesList.toArray(new byte[0][0]));
            return Objects.nonNull(pushNumbers) && !pushNumbers.equals(0L);
        }
    }

}
